#pragma once

/*房间类*/
#include "Common.hpp"
#include "OnlineManager.hpp"

using namespace ns_log;
const int ROWS = 15;
const int COLS = 15;
const int BLACK_CHESS = 1;
const int WHITE_CHESS = 2;
const int online_user_count = 2;

enum class ROOM_STATU
{
    GAME_START,
    GAME_OVER
};
enum class OPTION
{
    PUT_CHESS,
    CHAT
};

class GameRoom
{
private:
    // 房间id
    LL _room_id;
    // 房间状态
    ROOM_STATU _room_statu;
    // 游戏房间在线人数
    int _player_cnt;
    // 黑棋id
    LL _black_id;
    // 白棋id
    LL _white_id;
    // mysql操作句柄
    db::UserInfo *_table;
    // 在线用户管理
    OnlineManager *_online_manage;
    // 棋盘
    std::vector<std::vector<int>> _board;
    // 敏感词屏蔽
    util::Trie *_trieTree;
    // 选项集合
    std::unordered_map<std::string, OPTION> _opts;
private:
    // 通用的检查函数，用于检查某个方向上是否有五子连珠
    bool checkDirection(int x, int y, int dx, int dy, int who)
    {
        int color = who == (_black_id ? BLACK_CHESS : WHITE_CHESS);
        int count = 1;
        // 正向检查
        for (int i = 1; i < 5; ++i)
        {
            int newX = x + i * dx;
            int newY = y + i * dy;
            if (newX >= 0 && newX < ROWS && newY >= 0 && newY < COLS && _board[newX][newY] == color)
            {
                ++count;
            }
            else
                break;
        }
        // 反向检查
        for (int i = 1; i < 5; ++i)
        {
            int newX = x - i * dx;
            int newY = y - i * dy;
            if (newX >= 0 && newX < ROWS && newY >= 0 && newY < COLS && _board[newX][newY] == color)
            {
                ++count;
            }
            else
                break;
        }
        return count >= 5;
    }

    // 检查横向是否有五子连珠
    bool checkHorizontal(int x, int y, int who)
    {
        return checkDirection(x, y, 0, 1, who);
    }

    // 检查纵向是否有五子连珠
    bool checkVertical(int x, int y, int who)
    {
        return checkDirection(x, y, 1, 0, who);
    }

    // 检查正斜向是否有五子连珠
    bool checkPositiveDiagonal(int x, int y, int who)
    {
        return checkDirection(x, y, 1, 1, who);
    }

    // 检查反斜向是否有五子连珠
    bool checkNegativeDiagonal(int x, int y, int who)
    {
        return checkDirection(x, y, 1, -1, who);
    }

    // 判断是否胜利
    LL IsWin(int x, int y, int who)
    {
        _board[x][y] = (who == _black_id ? BLACK_CHESS : WHITE_CHESS);
        bool ret = (checkHorizontal(x, y, who) ||
                    checkVertical(x, y, who) ||
                    checkPositiveDiagonal(x, y, who) ||
                    checkNegativeDiagonal(x, y, who));
        if (ret == false)
            who = 0;
        return who;
    }
private:
    // 处理下棋逻辑
    Json::Value _HandlerChess(Json::Value &req)
    {
        Json::Value rsp = req;
        // 2.如果在，则判断房间在线人数是否 == 2，不满足就让在线的一方胜利
        LL current_id = req["uid"].asInt64();
        // 获取请求下棋的坐标
        int posx = req["row"].asInt();
        int posy = req["col"].asInt();

        if (_online_manage->GetConnByRoom(_black_id).first == false)
        {
            rsp["result"] = true;
            rsp["reason"] = "对方掉线啦,您已获胜!";
            rsp["winner"] = _white_id;
            _room_statu = ROOM_STATU::GAME_OVER;
            return rsp;
        }
        if (_online_manage->GetConnByRoom(_white_id).first == false)
        {
            rsp["result"] = true;
            rsp["reason"] = "对方掉线啦,您已获胜!";
            rsp["winner"] = _black_id;
            _room_statu = ROOM_STATU::GAME_OVER;
            return rsp;
        }
        // 3.下棋的位置是否合法
        if (posx < 0 || posx >= ROWS || posy < 0 || posy >= COLS || _board[posx][posy] != 0)
        {
            // 不合法
            rsp["result"] = false;
            rsp["reason"] = "当前位置已经存在棋子了!";
            return rsp;
        }
        // 4.判断是否胜利
        LL winner = IsWin(posx, posy, current_id);
        if (winner != 0)
        {
            rsp["result"] = true;
            rsp["reason"] = "恭喜您!您已获胜!";
            _room_statu = ROOM_STATU::GAME_OVER;
        }
        rsp["winner"] = winner; // 为0则无人胜出,交给外部判断
        return rsp;
    }

    Json::Value _HandlerChatMsg(Json::Value &req)
    {
        Json::Value rsp = req;
        // 屏蔽敏感词
        std::string msg = _trieTree->filterSensitiveWords(req["message"].asString());
        rsp["message"] = msg;
        rsp["result"] = true;
        return rsp;
    }
    // 处理未知请求
    void Unknown(Json::Value &req)
    {
        Json::Value rsp = req;
        rsp["result"] = false;
        rsp["reason"] = "未知请求";
        BoardCast(rsp);
    }
    // 处理房间号不匹配事件
    bool HandlerRoomNotMatch(Json::Value &req)
    {
        Json::Value rsp = req;
        LL roomid = req["room_id"].asInt64();
        if (_room_id != roomid)
        {
            rsp["result"] = false;
            rsp["reason"] = "房间号不匹配";
            BoardCast(rsp);
            return false;
        }
        return true;
    }
    // 处理下棋
    void HandlerChess(Json::Value &req)
    {
        Json::Value rsp = _HandlerChess(req);
        // 判断是否有用户胜利
        LL winner = rsp["winner"].asInt64(), loser = 0;
        if (winner != 0)
        {
            loser = (winner == _black_id ? _white_id : _black_id);
            _table->AddScoreAndCnt(winner, 10); // TODO
            _table->ReduceScore(loser, 10);     // TODO
        }
        // 广播
        BoardCast(rsp);
    }
    // 处理聊天逻辑
    void HandlerChatMsg(Json::Value &req)
    {
        Json::Value rsp = _HandlerChatMsg(req);
        BoardCast(rsp);
    }
    // 广播
    void BoardCast(Json::Value &rsp)
    {
        // 发送啥？序列化后的字符串信息!!
        std::string msg;
        util::JsonTool::Serialization(rsp, &msg);
        // 向谁发？房间内的所有连接!
        // 如何找到要发送的用户? 通过_online_manage获取！
        std::vector<WebSocketServer::connection_ptr> conns;
        _online_manage->GetAllConnByRoom(&conns);
        if (conns.size() != online_user_count)
        {
            LOG(ERROR, "广播时,房间内不存在2个及以上的用户连接\n");
            return;
        }
        // 怎么发？观察者模式!
        for (auto &conn : conns)
        {
            conn->send(msg);
        }
        return;
    }

public:
    GameRoom(LL room_id, db::UserInfo *table, OnlineManager *online_manger, util::Trie *trie_tree)
        : _room_id(room_id),
          _room_statu(ROOM_STATU::GAME_START),
          _player_cnt(0),
          _table(table),
          _online_manage(online_manger),
          _board(ROWS, std::vector<int>(COLS, 0)),
          _trieTree(trie_tree)
    {
        _opts.insert({"put_chess", OPTION::PUT_CHESS});
        _opts.insert({"chat", OPTION::CHAT});

        LOG(DEBUG, "房间创建done\n");
    }
    ~GameRoom()
    {
        LOG(DEBUG, "房间:%lld销毁done\n", _room_id);
    }
    LL RoomId()
    {
        return _room_id;
    }
    LL BlackId()
    {
        return _black_id;
    }
    LL WhiteId()
    {
        return _white_id;
    }
    ROOM_STATU RoomStatu()
    {
        return _room_statu;
    }
    int PlayCount()
    {
        return _player_cnt;
    }
    void SetBlackId(LL id)
    {
        _black_id = id;
        _player_cnt++;
    }
    void SetWhiteId(LL id)
    {
        _white_id = id;
        _player_cnt++;
    }
     // 处理玩家退出房间动作
    void ExitRoom(LL id)
    {
        Json::Value rsp;
        if (_room_statu == ROOM_STATU::GAME_START)
        {
            // 另一方胜利
            rsp["optype"] = "put_chess";
            rsp["result"] = true;
            rsp["reason"] = "对方掉线啦,您已获胜!";
            rsp["winner"] = id == _black_id ? _white_id : _black_id;
            rsp["row"] = -1;
            rsp["col"] = -1;
            rsp["room_id"] = _room_id;
            rsp["uid"] = id;
            // 广播给其他用户
            return BoardCast(rsp);
        }
        --_player_cnt;
        return;
    }
    // 状态机自动选择功能进行执行
    void HandlerRequest(Json::Value &req)
    {
        if (!HandlerRoomNotMatch(req))
        {
            // 房间不匹配内部处理响应后返回即可
            return;
        }
        std::string op = req["optype"].asString();
        switch (_opts[op])
        {
        case OPTION::PUT_CHESS:
            HandlerChess(req);
            break;
        case OPTION::CHAT:
            HandlerChatMsg(req);
            break;
        default:
            Unknown(req);
            break;
        }
        return;
    }
};
